home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
System
/
JPartial Resources
/
JPartialResources.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-12
|
10KB
|
374 lines
/*
* JPartialResources.c
*
* Jamie's partial resource calls that work under any system.
* Version 1.0.1
* © Copyright 1992-93 by Jamie R. McCarthy. All rights reserved.
* This code can be both distributed and used freely.
* Internet: k044477@kzoo.edu AppleLink: j.mccarthy
* Telephone: 800-421-4157 or US 616-665-7075 (9:00-5:00 Eastern time)
* I'm releasing this code with the hope that someone will get something
* out of it. Feedback of any sort, even just letting me know that you're
* using it, is greatly appreciated!
*
* Many thanks to Marco Piovanelli (piovanel@dsi.unimi.it) for pointing
* out that my extra FSRead was unnecessary, and for showing me his
* source code. One less disk access per call!
*
*
* "Be aware that having a copy of a resource in memory when you are using
* the partial resource routines may cause problems. If you have modified
* the copy in memory and then access the resource on disk using either
* the ReadPartialResource or WritePartialResource procedure, you will
* lose changes made to the copy in memory." - IM VI, 13-21
*
* By "copy in memory," this refers to having the entire resource loaded in.
* What it's saying is that ReadPartialResource and WritePartialResource
* consider themselves free to always purge (and maybe reload) the resource
* handle. Incidentally, this code doesn't do that--it reads directly from
* disk--so if you're under system 6, your copy of the resource is safe.
* (Memory may be moved, however, so if the resource is purgeable, it _may_
* be purged.)
*
*
* "...there's really no reason to access resources directly." - IM IV-16.
*
* Yeah right.
*
*/
/******************************/
#include "JPartialResources.h"
/******************************/
#include <GestaltEqu.h>
#include <Resources.h>
#include <Exceptions.h>
/******************************/
#if defined(__SYSEQU__)
#define SetResErr(x) ((*(short*)ResErr) = (x))
#elif defined(__LOMEM__)
#define SetResErr(x) (ResErr = (x))
#else
#define SetResErr(x) ((*(short*)0x0A60) = (x))
#endif
enum {
// See JPartialResources.h for comments. If a future Errors.h
// file does indeed define these values, you can delete this
// enum and replace references to "kErrXxx" with "xxx".
// Or not. It'll work fine the way it is. It's up to you.
kErrInputOutOfBounds = -190,
kErrWritingPastEnd = -189
} ;
/******************************/
#define kUninitializedFlag (42)
Boolean partialResourceCallsAreAvailable = kUninitializedFlag;
/******************************/
typedef struct {
unsigned long offsetToRsrcData;
unsigned long offsetToRsrcMap;
unsigned long lengthOfRsrcData;
unsigned long lengthOfRsrcMap;
} rsrcForkHeader;
typedef struct {
short rsrcID;
short offsetToRsrcName;
unsigned long rsrcAttrs : 8;
unsigned long offsetToRsrcData : 24;
Handle rsrcHndl;
} rsrcReference, *rsrcReferencePtr;
struct rsrcMap;
typedef struct rsrcMap rsrcMap, *rsrcMapPtr, **rsrcMapHndl;
struct rsrcMap {
rsrcForkHeader copyOfRsrcForkHeader;
rsrcMapHndl nextRsrcMapHndl;
short fileRefNum;
short fileAttrs;
unsigned short offsetToTypeList;
unsigned short offsetToNameList;
} ;
/******************************/
void determineWhetherPRCallsAreAvailable(void);
/******************************/
/*
* Just as _HomeResFile returns the refNum of a resource's file,
* so does getHomeRsrcMap() return a pointer to the resource
* file's map. Warning--any calls that may move memory,
* including system memory, invalidate the pointer!
*/
rsrcMapPtr getHomeRsrcMap(Handle theResource);
/*
* This uses RsrcMapEntry and getHomeRsrcMap() to find the
* offset from the beginning of a resource fork to the start
* of a resource's data. The offset points to the data itself,
* which is just past the longword that tells its size. If you
* happen to want the size too, use _SizeResource.
*/
long getRsrcDataOffsetIntoFile(Handle theResource);
long getRsrcFileMark(Handle theResource);
void setRsrcFileMark(Handle theResource, long newMark);
/******************************/
pascal void jReadPartialResource(Handle theResource, long offset, void *buffer, long count)
{
long rsrcSize;
if (partialResourceCallsAreAvailable == kUninitializedFlag) {
determineWhetherPRCallsAreAvailable();
}
rsrcSize = SizeResource(theResource);
if (ResError() != noErr || rsrcSize < 0) {
return ;
}
if (offset + count > rsrcSize) {
SetResErr(kErrInputOutOfBounds);
return ;
}
if (partialResourceCallsAreAvailable) {
ReadPartialResource(theResource, offset, buffer, count);
} else {
long theRsrcDataOffset;
long theRsrcDataLength;
OSErr theErr;
long oldMark;
oldMark = getRsrcFileMark(theResource);
theRsrcDataOffset = getRsrcDataOffsetIntoFile(theResource);
if (theRsrcDataOffset < 0) {
SetResErr(kErrInputOutOfBounds);
} else {
setRsrcFileMark(theResource, theRsrcDataOffset + offset);
theErr = FSRead(HomeResFile(theResource), &count, buffer);
setRsrcFileMark(theResource, oldMark);
}
}
}
pascal void jWritePartialResource(Handle theResource, long offset, void *buffer, long count)
{
long rsrcSize;
if (partialResourceCallsAreAvailable == kUninitializedFlag) {
determineWhetherPRCallsAreAvailable();
}
/*
* Disallow writing past the end of the resource;
* try to increase its size if possible.
*/
rsrcSize = SizeResource(theResource);
if (ResError() != noErr || rsrcSize < 0) {
return ;
}
if (SizeResource(theResource) < offset+count) {
jSetResourceSize(theResource, offset+count);
}
/*
* Check to be sure the size was successfully increased
* before continuing.
*/
if (ResErr != noErr || SizeResource(theResource) < offset+count) {
return ;
}
if (partialResourceCallsAreAvailable) {
WritePartialResource(theResource, offset, buffer, count);
} else {
long theRsrcDataOffset;
OSErr theErr;
long oldMark;
oldMark = getRsrcFileMark(theResource);
theRsrcDataOffset = getRsrcDataOffsetIntoFile(theResource);
if (theRsrcDataOffset < 0) return;
setRsrcFileMark(theResource, theRsrcDataOffset + offset);
theErr = FSWrite(HomeResFile(theResource), &count, buffer);
setRsrcFileMark(theResource, oldMark);
}
}
pascal void jSetResourceSize(Handle theResource, long size)
{
if (partialResourceCallsAreAvailable == kUninitializedFlag) {
determineWhetherPRCallsAreAvailable();
}
if (partialResourceCallsAreAvailable) {
SetResourceSize(theResource, size);
} else {
// Can't be done. Changing the size of a resource on disk
// means moving everything in the fork that follows it,
// which means calculating and updating a lot of data.
// Doing something wrong would destroy the file, too, which
// is a potential disaster I'd rather not flirt with.
SetResErr(kErrWritingPastEnd);
}
}
void determineWhetherPRCallsAreAvailable(void)
{
if (partialResourceCallsAreAvailable == kUninitializedFlag) {
OSErr theOSErr;
long theResponse;
partialResourceCallsAreAvailable = FALSE;
#define kAppleActuallyImplementedTheResourceMgrAttrSelectorInSystem7_0 (FALSE)
#if kAppleActuallyImplementedTheResourceMgrAttrSelectorInSystem7_0
theOSErr = Gestalt(gestaltResourceMgrAttr, &theResponse);
if (theOSErr == noErr) {
if ( (theResponse & 1L<<gestaltPartialRsrcs) != 0 ) {
partialResourceCallsAreAvailable = TRUE;
}
}
#else
theOSErr = Gestalt(gestaltSystemVersion, &theResponse);
if (theOSErr == noErr) {
if ( (theResponse & 0x0000FFFF) < 0x0710 ) {
// Pre-7.1, we can't trust gestaltResourceMgrAttr;
// we have to read the system version.
if ( (theResponse & 0x0000FFFF) >= 0x0700) {
partialResourceCallsAreAvailable = TRUE;
}
} else {
// In 7.1 and beyond, we can trust gestaltResourceMgrAttr;
// use it.
theOSErr = Gestalt(gestaltResourceMgrAttr, &theResponse);
if (theOSErr == noErr) {
if ( (theResponse & 1L<<gestaltPartialRsrcs) != 0 ) {
partialResourceCallsAreAvailable = TRUE;
}
}
}
}
#endif
}
}
/******************************/
rsrcMapPtr getHomeRsrcMap(Handle theResource)
{
rsrcMapHndl cMapHndl;
short theRefNum;
ASSERT(theResource != NULL);
theRefNum = HomeResFile(theResource);
cMapHndl = (rsrcMapHndl) TopMapHndl;
while ( (**cMapHndl).fileRefNum != theRefNum ) {
cMapHndl = (**cMapHndl).nextRsrcMapHndl;
if (cMapHndl == NULL) return NULL;
}
return *cMapHndl;
}
long getRsrcDataOffsetIntoFile(Handle theResource)
{
rsrcReferencePtr theRsrcRefPtr;
long theOffsetToRsrcRef;
long theOffsetToRsrcData;
rsrcMapPtr theRsrcMapPtr;
ASSERT(theResource != NULL);
theOffsetToRsrcRef = RsrcMapEntry(theResource);
theRsrcMapPtr = getHomeRsrcMap(theResource);
if (theRsrcMapPtr == NULL) {
theOffsetToRsrcData = -1;
} else {
theRsrcRefPtr = (rsrcReferencePtr)
(((char*)theRsrcMapPtr) + theOffsetToRsrcRef);
theOffsetToRsrcData = theRsrcRefPtr->offsetToRsrcData
+ 0x0100 // add in the length of the resource header
+ sizeof(long); // skip over the longword that tells the resource's length
}
return theOffsetToRsrcData;
}
long getRsrcFileMark(Handle theResource)
{
long theMark;
short theRefNum;
ASSERT(theResource != NULL);
theRefNum = HomeResFile(theResource);
if (ResError() != noErr) {
theMark = -1;
} else {
OSErr theErr;
theErr = GetFPos(theRefNum, &theMark);
if (theErr != noErr) {
theMark = -1;
}
}
return theMark;
}
void setRsrcFileMark(Handle theResource, long newMark)
{
long theMark;
short theRefNum;
ASSERT(theResource != NULL);
theRefNum = HomeResFile(theResource);
if (ResError() != noErr) {
return ;
} else {
/* theOSErr = */ SetFPos(theRefNum, fsFromStart, newMark);
}
}